package com.cityu.bac.rfidDriver;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * RFID Serial Command
 * @author shinelk
 *
 */

class Command {
	static byte startbyte = (byte) 0xBB;
    static byte endbyte = 0x7E;
    
    static byte typeCommand = 0x00;
    static byte typeResp = 0x01;
    static byte typeNotify = 0x02;
    static byte typeReserved = 0x03;
    
    static byte[] openCommand(){
        byte[] cmd = {(byte) 0xBB,0x00,0x03,0x00,0x01,0x00,0x7E};
        return cmd;
    }
    
    /**
     * Build Command
     */
    static byte[] buildCommand(byte cmdType,byte cmdOp,byte[] data){
        ByteArrayOutputStream cmdbos = new ByteArrayOutputStream();
        
        cmdbos.write(startbyte);
        cmdbos.write(cmdType);
        cmdbos.write(cmdOp);
        
        short len = (short) data.length;
        cmdbos.write((byte)(len & 0xFF00) >> 8);
        cmdbos.write((byte)(len & 0xFF));
        
        cmdbos.write(data,0,data.length);
        cmdbos.write(endbyte);
        try {
            cmdbos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        return cmdbos.toByteArray();
    }

    /***************************************
     * 
     * Start 
     * 
     * *************************************/
    static byte[] cmdStart()
    {
        byte[] cmd = { (byte) 0xBB, 0x00, 0x27, 0x00, 0x03, 0x22, (byte) 0xF0, (byte) 0xF3, 0x7e };
        return cmd;
    }
    static boolean respokStart(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, 0x27, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }
    /***************************************
     * 
     * Notification
     * 
     * *************************************/
    static byte[] parseNotif(byte[] notif)
    {
        if (notif[0] != 0xbb ||
            notif[1] != 0x02 ||
            notif[2] != 0x22 ||
            notif[notif.length - 1] != 0x7e)
            return null;
        int len = (int)notif[3] * 256 + (int)notif[4];
        if (len != notif.length - 6)
            return null;
        byte[] UII = new byte[len];
        for (int i = 0; i < UII.length; i++)
            UII[i] = notif[i + 5];
        return UII;
    }
    static boolean isNotifEnd(byte[] notif)
    {
        byte[] cmd = { startbyte, typeNotify, 0x27, 0x00, 0x01, 0x1F, endbyte };
        
        if (notif.length != cmd.length)
        {
            return false;
        }

        for (int i = 0; i < cmd.length; i++)
        {
            if (cmd[i] != notif[i])
            {
                return false;
            }
        }

        return true;
    }
    /***************************************
     * 
     * Stop
     * 
     * *************************************/
    static byte[] cmdStop()
    {
        byte[] cmd = { (byte) 0xBB, 0x00, 0x28, 0x00, 0x00, 0x7E };
        return cmd;
    }
    static boolean respokStop(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, 0x28, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }
    
    /***************************************
     * 
     * Write Tag
     * 
     * *************************************/
    static byte[] cmdWrite(byte[] oldPC_EPC, byte[] newPC_EPC, byte[] accessPassword)
    {
        if (accessPassword.length != 4)
        {
            return null;
        }
        int UL = oldPC_EPC.length;
        int DL = newPC_EPC.length;
        int PL = UL + DL + 11;
        byte[] cmd = new byte[PL + 6];
        cmd[0] = (byte) 0xBB; cmd[1] = 0x00; cmd[2] = 0x49;
        cmd[3] = (byte)(PL >> 8);//PL
        cmd[4] = (byte)(PL % 256);
        for (int i = 0; i < 4; i++)
            cmd[i + 5] = accessPassword[i];
        cmd[9] = (byte)(UL / 256);//UL
        cmd[10] = (byte)(UL % 256);
        for (int i = 0; i < UL; i++)
            cmd[i + 11] = oldPC_EPC[i];
        cmd[11 + UL] = 0x01;//MB
        cmd[12 + UL] = 0;//SA
        cmd[13 + UL] = 0x01;//SA
        cmd[14 + UL] = (byte)(DL >> 8);
        cmd[15 + UL] = (byte)(DL % 256);
        for (int i = 0; i < DL; i++)
            cmd[i + 16 + UL] = newPC_EPC[i];
        cmd[cmd.length - 1] = 0x7E;
        return cmd;
    }
    static boolean respokWrite(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, 0x49, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }
    /***************************************
     * 
     * Kill
     * 
     * *************************************/
    static byte[] cmdKill(byte[] PC_EPC, byte[] killPassword)
    {
        int PL = PC_EPC.length + 6;
        int UL = PC_EPC.length;
        byte[] cmd = new byte[PL + 6];
        cmd[0] = (byte) 0xBB; cmd[1] = 0x00; cmd[2] = 0x65;
        cmd[3] = (byte)(PL >> 8);//PL
        cmd[4] = (byte)(PL % 256);
        for (int i = 0; i < 4; i++)
            cmd[i + 5] = killPassword[i];
        cmd[9] = (byte)(UL >> 8);//UL
        cmd[10] = (byte)(UL % 256);
        for (int i = 0; i < UL; i++)
            cmd[i + 11] = PC_EPC[i];
        cmd[cmd.length - 1] = 0x7E;
        return cmd;
    }
    static boolean respokKill(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, 0x65, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }
    /***************************************
     * 
     * Set PA Power
     * 
     * *************************************/
    static byte[] cmdSetPAPower(short power)
    {
        byte[] cmd = { (byte) 0xBB, 0x00, (byte) 0xB2, 0x00, 0x02, 0x00, 0x00, 0x7E };
        cmd[5] = (byte)(power / 256);
        cmd[6] = (byte)(power % 256);
        return cmd;
    }
    static boolean respokSetPAPower(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, (byte) 0xB2, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }
    /***************************************
     * 
     * Set Power
     * @throws Exception 
     * 
     * *************************************/
    static byte[] cmdSetReaderPower(short power) throws Exception
    {
        if (power > 1000 || power < -1350)
        {
            throw new Exception("Power is out of range: " + power);
        }

        byte[] cmd = { (byte) 0xBB, 0x00, (byte) 0xB6, 0x00, 0x02, 0x00, 0x00, 0x7E };
        cmd[5] = (byte)(power >> 8);
        cmd[6] = (byte)(power % 256);
        return cmd;
    }
    static boolean respokSetReaderPower(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, (byte) 0xB6, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }

    /***************************************
     * 
     * Set PA Offset
     * 
     * *************************************/
    static byte[] cmdSetPAOffset(short offset)
    {
        byte[] cmd = { (byte) 0xBB, 0x00, (byte) 0xB8, 0x00, 0x02, 0x00, 0x00, 0x7E };
        cmd[5] = (byte)(offset >> 8);
        cmd[6] = (byte)(offset % 256);
        return cmd;
    }
    static boolean respokSetPAOffset(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, (byte) 0xB8, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }

    /***************************************
     * 
     * Set Power Class
     * 
     * *************************************/
    static byte[] cmdSetPowerClass(int powerClass)
    {
        if (powerClass < 0 || powerClass > 3)
            return null;
        byte[] cmd = { (byte) 0xBB, 0x00, (byte) 0xBA, 0x00, 0x01, 0x00, 0x7E };
        cmd[5] = (byte)powerClass;
        return cmd;
    }
    static boolean respokSetPowerClass(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, (byte) 0xBA, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }

    /***************************************
     * 
     * Set Region
     * 
     * *************************************/
    static byte[] cmdSetRegion(byte region)
    {
        if (region != 0x01 && region != 0x02 && region != 0x04 && region != 0x08 && region != 0x16 && region != 0x32)
            return null;
        byte[] cmd = { (byte) 0xBB, 0x00, 0x07, 0x00, 0x01, 0x00, 0x7E };
        cmd[5] = region;
        return cmd;
    }
    static boolean respokSetRegion(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, 0x07, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }

    /***************************************
     * 
     * Get Region
     * 
     * *************************************/
    static byte[] cmdGetRegion()
    {
        byte[] cmd = { (byte) 0xBB, 0x00, 0x06, 0x00, 0x00, 0x7E };
        return cmd;
    }
    static byte parseRegion(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, 0x06, 0x00, 0x01 };
        if (resp.length != 7)
            return 0;
        for (int i = 0; i < 5; i++)
            if (resp[i] != cmd[i])
                return 0;
        if (resp[6] != 0x7E)
            return 0;
        byte region = resp[5];
        if (region != 0x01 && region != 0x02 && region != 0x04 && region != 0x08 && region != 0x16 && region != 0x32)
            return 0;
        return region;
    }

    /***************************************
     * 
     * Set Channel
     * 
     * *************************************/
    static byte[] cmdSetChannel(byte channelNumber, byte channelOffset)
    {
        byte[] cmd = { (byte) 0xBB, 0x00, (byte) 0xAB, 0x00, 0x02, 0x00, 0x00, 0x7E };
        cmd[5] = channelNumber;
        cmd[6] = channelOffset;
        return cmd;
    }
    static boolean respokSetChannel(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, (byte) 0xAB, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }

    /***************************************
     * 
     * Get Channel
     * 
     * *************************************/
    static byte[] cmdGetChannel()
    {
        byte[] cmd = { (byte) 0xBB, 0x00, (byte) 0xAA, 0x00, 0x00, 0x7E };
        return cmd;
    }
    static byte parseChannel(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, (byte) 0xAA, 0x00, 0x02 };
        if (resp.length != 8)
            return 0;
        for (int i = 0; i < 5; i++)
            if (resp[i] != cmd[i])
                return 0;
        if (resp[7] != 0x7E)
            return 0;
        byte channel = resp[5];
        return channel;
    }
    
    /*
     * cmd QRY_PARM
     */
    static byte[] cmdSetQryParm(int qry){
        byte[] cmd = { (byte)0xBB,0x00,(byte)0x0E,0x00,0x02,0x10,0x00,0x7E};
        byte q = (byte) (qry & 0x0f);
        cmd[6] = (byte) (q<<3);
        return cmd;
    }
    
    static boolean respokSetQryParm(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, (byte) 0x0E, 0x00, 0x01, 0x00, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }


    /***************************************
     * 
     * Extend Expect Response
     * 
     * For most commands, the relative response contains the same command code.
     * But for some commands, the relative response would be special when executing failed.
     * For example, when write tag failed, the received response would be {BB 01 FF 00 01 10 7E}.
     * This function aims to deal with such cases.
     * 
     * *************************************/
    static boolean isExtResp(byte code, byte[] resp)
    {
        switch (code)
        {
            case 0x49: //Write Tag
                byte[] respSuccess = { (byte) 0xBB, 0x01, (byte) 0xFF, 0x00, 0x01, 0x10, 0x7E };
                byte[] respFailed = { (byte) 0xBB, 0x01, (byte) 0xFF, 0x00, 0x01, 0x15, 0x7E };
                if (Utils.equalbyteArr(respSuccess, resp))
                {
                    return true;
                }
                else if (Utils.equalbyteArr(respFailed, resp))
                {
                    return true;
                }
                else
                {
                    return false;
                }

            default:
                return false;
        }
    }
    

    /***************************************
     * 
     * Switch to Barcode / RFID
     * 
     * 
     * *************************************/
    static byte[] cmdSwitchToRFID()
    {
        byte[] cmd = { (byte) 0xBB, 0x00, 0x12, 0x00, 0x01, 0x01, 0x7E };
        return cmd;
    }

    static byte[] cmdSwitchToBarcode()
    {
        byte[] cmd = { (byte) 0xBB, 0x00, 0x12, 0x00, 0x01, 0x00, 0x7E };
        return cmd;
    }

    static boolean respokSwitchDevice(byte[] resp)
    {
        byte[] cmd = { (byte) 0xBB, 0x01, 0x12, 0x00, 0x01, 0x01, 0x7E };
        if (resp.length != cmd.length)
            return false;
        for (int i = 0; i < cmd.length; i++)
            if (cmd[i] != resp[i])
                return false;
        return true;
    }
    
    static byte[] cmdClickButton(){
        byte[] cmd = { (byte) 0xBB, 0x00, 0x13, 0x00, 0x01, 0x01, 0x7E };
        return cmd;
    }
    
    static byte[] cmdReleaseButton(){
        byte[] cmd = { (byte) 0xBB, 0x00, 0x13, 0x00, 0x01, 0x00, 0x7E };
        return cmd;
    }
}
